var extend = require('extend');
var lodash = require('lodash');
var DataStore = require('nedb');

var DB = function (daoOptions, cb) {
    var self = this;
    var options = extend({}, daoOptions.options);
    options.autoload = true;
    options.onload = function (err) {
        if (err) {
            if (cb) {
                return cb(err);
            }
            throw err;
        }
        return (<any>Promise.resolve(daoOptions.index).then(function (index) {
            return Promise.all(lodash.map(index, function (_index) {
                return (<any>Promise).promisify(self.datastore.ensureIndex.bind(self.datastore))(_index);
            }));
        }).then(function(){
            return self;
        })).callback(cb);
    };
    this.datastore = new DataStore(options);
    if (options.autocompactionInterval) {
        this.datastore.persistence.setAutocompactionInterval(options.autocompactionInterval);
    }
};

DB.prototype.insert = function (doc) {
    return (<any>Promise).promisify(this.datastore.insert.bind(this.datastore))(doc);
};

DB.prototype.update = function (doc) {
    //if(doc.terminalBindId == undefined || doc.terminalBindId == null) return ;
    var updatePromise = (<any>Promise).promisify(this.datastore.update.bind(this.datastore));
    return doc._id ? updatePromise({_id: doc._id}, lodash.omit(doc, '_id')) :
        doc.id ? updatePromise({id: doc.id}, doc, {upsert: true}) : updatePromise(doc, doc, {upsert: true});
};

DB.prototype.remove = function (doc, options) {
    return (<any>Promise).promisify(this.datastore.remove.bind(this.datastore))(doc, options || {});
};

DB.prototype.count = function (query) {
    return (<any>Promise).promisify(this.datastore.count.bind(this.datastore))(query);
};

DB.prototype.get = function (query, must) {
    return (<any>Promise).promisify(this.datastore.find.bind(this.datastore))(query).then(function (results) {
        if (results.length > 1) {
            throw new Error('get returns more than 1.');
        }
        if (must && !results.length) {
            throw new Error('must has but not one');
        }
        return results.length ? results[0] : null;
    });
};

DB.prototype.find = function (expr, proj, sort, skip, limit) {
    var find = this.datastore.find(expr);
    if (!lodash.isEmpty(proj)) {
        find = find.projection(proj);
    }
    if (!lodash.isEmpty(sort)) {
        find = find.sort(sort);
    }
    if (skip) {
        find = find.skip(skip);
    }
    if (limit) {
        find = find.limit(limit);
    }
    return (<any>Promise).promisify(find.exec.bind(find))();
};


DB.prototype.clear = function (query) {
    return (<any>Promise).promisify(this.datastore.remove.bind(this.datastore))(query, {multi: true});
};

module.exports = DB;